package src.cs2340.todoApp;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

/**
 * @version 1.0 March 19, 2012
 * This will test the Account class methods with Kristian's and Cthulu's accounts
 * on the same database.
 */
public class AccountTest {
	Database db, db2, db3;
	Account acc, acc2, acc3, acc4, acc5, acc6;
	
	/**
	 * @author Kristian
	 * @throws Exception
	 */
	@Before
	public void setUp() throws Exception {
		db = Database.getInstance();
		acc = new Account("Kristian.E64", "password", "Kristian", 
				"Kristian.E64@gmail.com", db);
		acc2 = new Account("Cthulu", "tentacles", "Cthulu",
				"Cthulu@Apocalypse.net", db);
		
		db2 = Database.getInstance();
		acc3 = new Account("Jordan", "passwordJ", "Aizent", "aizent@gmail.com", db2);
		acc4 = new Account("Rochelle", "passwordR", "LoboGirl", "rochelle.l.lobo@gmail.com", db2);
		
		db3 = Database.getInstance();
		acc5 = new Account("Rahul", "passwordR", "sheth38", "rajul.sheth@gmail.com", db3);
		acc6 = new Account("Bob", "passwordB", "bob38", "bob.waters@bob.com", db3);
	}
	
	/**
	 * @author Jordan
	 * Will test the register method to make sure the accounts are registering without 
	 * runtime errors.  Uses database's login method to make sure account registers.
	 * 
	 * Utilizes Database 1.
	 */
	@Test
	public void testRegister()
	{
		assertFalse("Kristian hasn't registered yet.", db.login(acc));
		
		try {
			acc.register();
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		assertTrue("Kristian's registered.  He should be able to login.", db.login(acc));
		assertFalse("Cthulu hasn't registered yet.", db.login(acc2));
		
		//now we register Cthulu.
		try {
			acc2.register();
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		assertTrue("Cthulu registering shouldn't affect Kristian being in the database",
				db.login(acc));
		assertTrue("Cthulu should be in the database upon registering.", db.login(acc2));
	}
	
	/**
	 * @author Jordan
	 * This is the test for the login method.  It uses the Register method, so if that
	 * test passes and this one doesn't, then login has the problem.
	 * 
	 * Will test Database 2.
	 */
	@Test
	public void testLogin()
	{
		acc3.login();
		assertFalse("You can't have logged in yet because you haven't registered.",
				acc3.loginFlag);
		
		try {
			acc3.register();//register Jordan
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		acc3.login();
		assertTrue("Jordan should be recognized in the database and should log in" +
				"normally.  Otherwise, he didn't register correctly", acc3.loginFlag);
		
		acc4.login();
		assertFalse("Rochelle hasn't yet registered.", acc4.loginFlag);
		
		try {
			acc4.register();//now we register Rochelle.
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//Both Jordan and Rochelle should now be able to login and logout.
		acc3.login();//Kristian's logging in a second time, shouldn't change loginFlag
		acc4.login();
		
		assertTrue("Jordan has logged in again. Login flag shouldn't change", acc3.loginFlag);
		assertTrue("Rochelle has logged in.", acc4.loginFlag);
		
		assertTrue("Rochelle has logged in again!", acc4.login());
		assertTrue("and again!", acc4.login());
		acc4.login();
		acc4.login();
		assertTrue("Really, it shouldn't matter by now!", acc4.login());
		
		assertTrue("Doesn't matter how many times Cthulu logs in, loginFlag remains the" +
				"same.", acc4.loginFlag);
		
		acc4.login();
		assertTrue("Just making sure loginFlag doesn't change when you check it" +
				"3 times...after logging in a seventh time!!!", acc4.loginFlag);
		assertTrue(acc4.loginFlag);
		assertTrue(acc4.loginFlag);
		
		assertTrue("working with this database shouldn't interfere with the other database," +
				"Kristian is in Database1, he should still be there.", acc.login());
		assertTrue("same for Cthulu", acc2.login());
	}

	/**
	 * @author Jordan
	 * Will verify with database that accounts that haven't registered don't log in.
	 * Those that do will login when login() is called.  Logout returns false no
	 * matter what.  
	 * 
	 * Also ensures that accounts already existing in a database can't
	 * register again.  Makes sure that information with multiple accounts is
	 * handled properly (not mixed up between accounts) when the database contains 
	 * more than one account that are logged in at the same time at one point in time.
	 * 
	 * This class assumes that register and login are working.  If you're having problems
	 * with login and register when the other two tests are working, then logout is creating
	 * problems.
	 * 
	 * Will test Database 3.
	 */
	@Test
	public void testLogout() {
		assertFalse("Rahul hasn't logged in to logout.  Heck, he haven't even registered",
				acc5.logout());
		assertFalse("same for Bob", acc6.logout());
		acc5.login();
		assertFalse("Rahul can't have logged in yet because he hasn't registered.",
				acc5.loginFlag);
		
		assertTrue("Jordan, however, is registered into database2, so he should still be " +
				"there", acc3.login());
		
		try {
			acc5.register();//register my account
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		acc.login();
		assertTrue("Kristian.E64 should still be recognized in the database and" + 
				"should log in normally.", acc.loginFlag);
		assertFalse("Logout should be false anyway.", acc.logout());
		assertFalse("Rahul should still return false for logging out", acc5.logout());
		assertFalse("Bob should too even though he hasn't registered.", acc6.logout());
		
		acc6.login();
		assertFalse("Bob hasn't yet registered.", acc6.loginFlag);
		
		//we can't register Kristian again
		try 
		{
			acc.register();
	        assertTrue("You failed to throw the exception properly for Kristian" +
	                "trying to register again.", false);
	    } catch (AddAccountException aae) {
	         assertTrue(true);
	          
	         /*if a whole bunch of errors print to the console, then 
	         the stack trace is printing properly.*/
	         aae.printStackTrace();
	      }
		
		try {
			acc6.register();//now we register Bob.
		} catch (AddAccountException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//Both Rahul and Bob should now be able to login and logout.
		acc5.login();
		acc6.login();
		
		assertTrue("Rahul has logged in.", acc5.loginFlag);
		assertTrue("Bob has logged in.", acc6.loginFlag);
		
		assertFalse("Rahul has logged out.", acc5.logout());
		assertFalse("Bob has logged out.", acc6.logout());
	}

	/**
	 * @author Rahul
	 * Makes sure that the user names for two accounts are stored
	 * and verified properly.  Wrong user names are verified 
	 * as wrong.  Makes sure correct ones are recognized as correct.
	 */
	@Test
	public void testVerifyUsername() {
		//testing Kristian's username
		assertTrue(acc.verifyUsername("Kristian.E64"));
		assertFalse(acc.verifyUsername("Kristian"));
		assertFalse(acc.verifyUsername("Cthulu"));
		
		//testing Cthulu's username
		assertTrue(acc2.verifyUsername("Cthulu"));
		assertFalse(acc2.verifyUsername("Kristian.E64"));
		
	}
	
	/**
	 * @author Rahul
	 * Makes sure the passwords are correct.
	 */
	@Test
	public void testVerifyPassword() {
		
		//testing Kristian's password
		assertFalse(acc.verifyPassword("password1"));
		assertFalse(acc.verifyPassword("tentacles"));
		assertTrue(acc.verifyPassword("password"));
		
		//testing Cthulu's password
		assertTrue(acc2.verifyPassword("tentacles"));
		assertFalse(acc2.verifyPassword("password1"));
	}

	/**
	 * @author Rahul
	 * This method takes advantage of the fact that two accounts are equal
	 * if their username and password are equal.  
	 * 
	 * In other words, two accounts with identical usernames and slightly 
	 * different passwords are different.
	 * 
	 * And two accounts with the same usernames and passwords but different 
	 * names and email addresses are identical.
	 * 
	 * For example:
	 * acc3 should be identical to Kristian's account due to same username
	 * and password, even though the new account's name is Pikachu.
	 * 
	 * acc4 uses a slightly different password, even though all the other info
	 * is the same, so it's different from acc.
	 */
	@Test
	public void testEqualsObject() {
		//creating Kristian copies
		Account acc3 = new Account("Kristian.E64", "password", "Pikachu", 
				"Kristian.E64@gmail.com", db);
		Account acc4 = new Account("Kristian.E64", "password1", "Kristian",
				"Kristian.E64@gmail.com", db);
		
		//creating Cthulu copies
		Account acc5 = new Account("Cthulu", "password", "Cthulu", 
				"Cthulu@Apocalypse.net", db);
		Account acc6 = new Account("Cthulu", "tentacles", "Mewtwo",
				"Mewtwo@Ditto.com", db);
		
		assertTrue(acc.equals(acc3));
		assertFalse(acc.equals(acc4));
		
		assertFalse(acc2.equals(acc5));
		assertTrue(acc2.equals(acc6));
	}

	/**
	 * @author Rahul
	 * I will add find(TaskItem t) to TaskList, then use that method to test whether 
	 * a TaskItem's been added to an Account.  For the time being, let's create some tasks
	 * and add them to the TaskLists of each Account.
	 */
	@Test
	public void testAddTaskItem() 
	{ 
		Location namek = new Location("Sparta", 24, "Namek", "Namek", "Namek", 300);
		Date fatedDay = new Date(30, 10, 1997, 12, 35, Date.Meredian.PM); // 10/30/1997 at 12:35pm
		TaskItem task1 = new TaskItem("Defeat Frieza", namek, fatedDay, 
				"Destroy Frieza for his injustice against the universe", "Be-A-Hero", false);
		
		Location home = new Location("Neptune", 64, "Derek Street", "Boca Raton", "FL", 9000);
		Date day = new Date(3, 28, 2013, 1, 45, Date.Meredian.AM); //3/28/2013 at 1:45am
		TaskItem task2 = new TaskItem("Basketball/circular logic w/ Derek", home, day, 
				"Play basketball with cousin and team up with him to annoy " +
				"family with circular logic", "cousin :D", false);
		
		//Kristian gets 2 tasks, task2 gets added to both Kristian AND Cthulu.
		acc.addTaskItem(task1);
		acc.addTaskItem(task2);
		acc2.addTaskItem(task2);
	}	
}
